
#include "gui_layout.h"

using namespace WtfGui;


/**** Advanced Layout ****/

AdvancedLayout::AdvancedLayout(const IWidgetLayout::tRef& rBaseLayout, const LayoutRule::tRef &rPosX, const LayoutRule::tRef &rPosY,
							   const LayoutRule::tRef &rSizeX, const LayoutRule::tRef &rSizeY)
: WtfEngine::GcObject(), mrBaseLayout(&*rBaseLayout) {
	mvrPositionLayout[0] = rPosX;
	mvrPositionLayout[1] = rPosY;
	mvrSizeLayout[0] = rSizeX;
	mvrSizeLayout[1] = rSizeY;

	AdvancedLayout::tRef rThis = this;
	mvrPositionLayout[0]->Init(rThis);
	mvrPositionLayout[1]->Init(rThis);
	mvrSizeLayout[0]->Init(rThis);
	mvrSizeLayout[1]->Init(rThis);
	
	Update();
};

void AdvancedLayout::Update() {
	if(mrBaseLayout.isNull()) {
		Destroy();
	} else {
		iVector2D vSize = mrBaseLayout->getSize(), vPos = mrBaseLayout->getPosition();
		mrBaseLayout->setPosition(iVector2D(mvrPositionLayout[0]->getValue(vPos[0], 0), mvrPositionLayout[1]->getValue(vPos[1], 1)));
		mrBaseLayout->setSize(iVector2D(mvrSizeLayout[0]->getValue(vSize[0], 0), mvrSizeLayout[1]->getValue(vSize[1], 1)));
	};
};

void AdvancedLayout::Destroy() {
	mvrPositionLayout[0]->Destroy(this);
	mvrPositionLayout[1]->Destroy(this);
	mvrSizeLayout[0]->Destroy(this);
	mvrSizeLayout[1]->Destroy(this);
};


/**
 * Layout decorators must listen on their parent layout for changes. Listener is installed here.
 **/
void AdvancedLayout::LayoutDecorator::Init(const WtfGui::AdvancedLayout::tRef &rLayout) {
	mrParentLayout->AddObserver(new WtfEngine::ObjFunctor<AdvancedLayout>(rLayout, &AdvancedLayout::Update));
	LayoutRule::Init(rLayout);
};
void AdvancedLayout::LayoutDecorator::Destroy(const WtfGui::AdvancedLayout::tRef &rLayout) {
	mrParentLayout->RemoveObserver(new WtfEngine::ObjFunctor<AdvancedLayout>(rLayout, &AdvancedLayout::Update));
	LayoutRule::Destroy(rLayout);
};



/**** Layout Container ****/

LayoutContainer::LayoutContainerCreationOptions::LayoutContainerCreationOptions()
: iElementSpacing(2), iMinSize(-1), iMaxSize(-1), bWrap(true)
{
};

LayoutContainer::LayoutContainer(const LayoutContainer::tCreationOptions& opts, int axis)
: WidgetContainer(""), miElementSpacing(opts.iElementSpacing), miMinSize(opts.iMinSize), miMaxSize(opts.iMaxSize),
  miAxis(axis), mbWrap(opts.bWrap)
{
};

void LayoutContainer::UpdateLayout() {
	iVector2D	vPos;
	iVector2D	vSize;
	int			iOppAxis = 1 - miAxis;

	for(tChildList::const_iterator i = mChildren.begin(); i != mChildren.end(); i++) {
		if((miMaxSize != -1) && (vPos[miAxis] + miElementSpacing + (*i)->getSize()[miAxis]) > miMaxSize) {
			if(mbWrap) {
				vPos[iOppAxis] += vSize[iOppAxis] + miElementSpacing;
				vSize[miAxis] = maxf(vSize[miAxis], vPos[miAxis] + miElementSpacing);
				vPos[miAxis] = 0;
				vSize[iOppAxis] = 0;
			};
		};

		// Add spacing
		vPos[miAxis] += miElementSpacing;
		
		// Position element
		(*i)->Move(vPos);
		vPos[miAxis] += (*i)->getSize()[miAxis];

		vSize[iOppAxis] = maxf((*i)->getSize()[iOppAxis], vSize[iOppAxis]);
	};

	// Resize container
	vSize[miAxis] = maxf(vSize[miAxis], vPos[miAxis] + miElementSpacing);
	vSize[miAxis] = miMaxSize < 0 ? vSize[miAxis] : minf(vSize[miAxis], miMaxSize);
	vSize[iOppAxis] += vPos[iOppAxis];
	setInnerSize(vSize);
};

void LayoutContainer::AddChild(const AbstractWidget::tRef& rChild) {
	WidgetContainer::AddChild(rChild);
	UpdateLayout();
};

void LayoutContainer::RemoveChild(const AbstractWidget::tRef& rChild) {
	WidgetContainer::RemoveChild(rChild);
	UpdateLayout();
};

void LayoutContainer::Clear() {
	WidgetContainer::Clear();
	UpdateLayout();
};

void LayoutContainer::OnResize() {
	Widget::OnResize();
};

void LayoutContainer::OnChildResize() {
	UpdateLayout();
};



/**** Scrolling ****/

ScrollLayout::ScrollLayoutCreationOptions::ScrollLayoutCreationOptions()
: LayoutContainer::tCreationOptions(), WheelScrollStep(30) {
};

ScrollLayout::ScrollLayout(const ScrollLayout::tCreationOptions& opts, int axis)
: LayoutContainer(opts, axis), miWheelScrollStep(opts.WheelScrollStep),
  miAmtScrolled(0)
{
};

void ScrollLayout::Paint(float d) {
	tChildList::reverse_iterator i;
	iVector2D vScroll;
	iVector2D vSize = getInnerSize();

	vScroll[miAxis] = miAmtScrolled;
	glTranslatef(vScroll[0], vScroll[1], 0);

	// Draw child widgets
	for(i = mChildren.rbegin(); i != mChildren.rend(); i++){
		iVector2D vPos = (*i)->getRealPosition() - vScroll;
		if( (vPos[0] + (*i)->getRealSize()[0]) > 0 && (vPos[1] + (*i)->getRealSize()[1]) > 0 &&
			vPos[0] < getInnerSize()[0] && vPos[1] < vSize[1])
		{
			d = d / 2;
			glTranslatef(0, 0, d);
			//SetupClipping();
			(*i)->Draw(d / 2);
		};
	};
};

bool ScrollLayout::OnMouseUp(const iVector2D &vPos, WtfEngine::tMouseButton btn) {
	iVector2D vChildPos = vPos;
	vChildPos[miAxis] += miAmtScrolled;

	switch(btn) {
		case WtfEngine::MOUSE_SCROLLUP:
			if(miAmtScrolled > 0) {
				miAmtScrolled = maxf(0, miAmtScrolled - miWheelScrollStep);
				return true;
			} else {
				return LayoutContainer::OnMouseUp(vChildPos, btn);
			};
		case WtfEngine::MOUSE_SCROLLDOWN:
		{
			// TODO: cache content size (slow calculation)
			int iMaxScroll = getContentSize()[miAxis] - getSize()[miAxis];
			if(miAmtScrolled < iMaxScroll) {
				miAmtScrolled = minf(iMaxScroll, miAmtScrolled + miWheelScrollStep);
				return true;
			} else {
				return LayoutContainer::OnMouseUp(vChildPos, btn);
			};
		}
		default:
			return LayoutContainer::OnMouseUp(vChildPos, btn);
	};
};


/**** Positioners ****/

void LayoutPositioner::Default::PositionWidget(AbstractWidget* rWidget, const WtfGui::WidgetContainer::tRef &rParent, int axis) {
	iVector2D vPos(rWidget->getPosition());
	vPos[axis] = miOffset;
	rWidget->Move(vPos);
};

void LayoutPositioner::Inverted::PositionWidget(AbstractWidget* rWidget, const WtfGui::WidgetContainer::tRef &rParent, int axis) {
	iVector2D vPos(rWidget->getPosition());
	vPos[axis] = rParent->getInnerSize()[axis] - (miOffset + rWidget->getSize()[axis]);
	rWidget->Move(vPos);
};

void LayoutPositioner::Centre::PositionWidget(AbstractWidget* rWidget, const WtfGui::WidgetContainer::tRef &rParent, int axis) {
	iVector2D vPos(rWidget->getPosition());
	vPos[axis] = ((rParent->getInnerSize()[axis] - rWidget->getSize()[axis]) / 2.0) + miOffset;
	rWidget->Move(vPos);
};

LayoutPositioner::LayoutPositioner(AbstractWidget *pWidget, const IPositioner::tRef& rXPos, const IPositioner::tRef& rYPos)
: WidgetWrapper(pWidget), mvrPositioner(rXPos, rYPos) {
};

void LayoutPositioner::Update() {
	WidgetContainer::tRef rParent = getParent();
	if(!rParent.isNull()) {
		mvrPositioner[0]->PositionWidget(this, rParent, 0);
		mvrPositioner[1]->PositionWidget(this, rParent, 1);
	};
};

void LayoutPositioner::OnInsert(WidgetContainer* pParent) {
	WidgetWrapper::OnInsert(pParent);
	Update();
};

void LayoutPositioner::OnResize() {
	Update();
	WidgetWrapper::OnResize();
};

void LayoutPositioner::OnParentResize() {
	Update();
	WidgetWrapper::OnParentResize();
};


/**** Layout Hider ****/

LayoutHider::LayoutHiderCreationOptions::LayoutHiderCreationOptions()
: LayoutContainer::tCreationOptions() {
	bWrap = false;
};

LayoutHider::LayoutHider(const LayoutHider::tCreationOptions &opts, int axis, AbstractWidget::tRefa rHider)
: LayoutContainer(opts, axis), mrHider(new HiderAction(&*rHider, this))
{
	AddChild(mrHider);
};

bool LayoutHider::Toggle() {
	if(miMaxSize == miMinSize && miMaxSize != -1) {
		Show();
		return true;
	} else {
		Hide();
		return false;
	};
};

void LayoutHider::Show() {
	miMaxSize = -1;
	UpdateLayout();
};

void LayoutHider::Hide() {
	miMaxSize = miMinSize = mrHider->getSize()[miAxis];
	UpdateLayout();
};

void LayoutHider::Clear() {
	LayoutContainer::Clear();
	AddChild(mrHider);
};

LayoutHider::HiderAction::HiderAction(WtfGui::AbstractWidget *pWidget, WtfGui::LayoutHider *pHider)
: WidgetWrapper(pWidget), mpHider(pHider) {
};

bool LayoutHider::HiderAction::OnMouseDown(const iVector2D &vPos, WtfEngine::tMouseButton btn) {
	if(WidgetWrapper::OnMouseDown(vPos, btn)) {
		return true;
	} else if(btn == WtfEngine::MOUSE_LEFT) {
		mpHider->Toggle();
		return true;
	} else {
		return false;
	};
};
